Imports System
Imports System.Configuration.Provider
Imports System.Security.Permissions
Imports System.Web
Imports System.Web.UI.WebControls.WebParts
Imports System.Collections.Specialized
Imports System.Security.Cryptography
Imports System.Text
Imports System.IO

Public Class FilePersonalizationProvider
    Inherits PersonalizationProvider

    Public Overrides Property ApplicationName() As String
        Get
            Throw New NotSupportedException
        End Get
        Set(ByVal value As String)
            Throw New NotSupportedException
        End Set
    End Property

    Public Overrides Sub Initialize(ByVal name As String, ByVal config As NameValueCollection)
        ' Verify that config isn't null
        If (config Is Nothing) Then
            Throw New ArgumentNullException("config")
        End If
        If String.IsNullOrEmpty(name) Then
            name = "TextFilePersonalizationProvider"
        End If
        ' Add a default "description" attribute to config if the
        ' attribute doesn't exist or is empty
        If String.IsNullOrEmpty(config("description")) Then
            config.Remove("description")
            config.Add("description", "Text file personalization provider")
        End If
        ' Call the base class's Initialize method
        MyBase.Initialize(name, config)
        ' Throw an exception if unrecognized attributes remain
        If (config.Count > 0) Then
            Dim attr As String = config.GetKey(0)
            If Not String.IsNullOrEmpty(attr) Then
                Throw New ProviderException(("Unrecognized attribute: " + attr))
            End If
        End If
        ' Make sure we can read and write files in the
        ' ~/App_Data/Personalization_Data directory
        Dim permission As FileIOPermission = New FileIOPermission(FileIOPermissionAccess.AllAccess, HttpContext.Current.Server.MapPath("~/App_Data/Personalization_Data"))
        permission.Demand()
    End Sub

    Protected Overrides Sub LoadPersonalizationBlobs(ByVal webPartManager As WebPartManager, ByVal path As String, ByVal userName As String, ByRef sharedDataBlob() As Byte, ByRef userDataBlob() As Byte)
        ' Load shared state
        Dim reader1 As StreamReader = Nothing
        sharedDataBlob = Nothing
        Try
            reader1 = New StreamReader(GetPath(Nothing, path))
            sharedDataBlob = Convert.FromBase64String(reader1.ReadLine)
        Catch ex As FileNotFoundException
            ' Not an error if file doesn't exist
        Finally
            If (Not (reader1) Is Nothing) Then
                reader1.Close()
            End If
        End Try
        ' Load private state if userName holds a user name
        If Not String.IsNullOrEmpty(userName) Then
            Dim reader2 As StreamReader = Nothing
            userDataBlob = Nothing
            Try
                reader2 = New StreamReader(GetPath(userName, path))
                userDataBlob = Convert.FromBase64String(reader2.ReadLine)
            Catch ex As FileNotFoundException
                ' Not an error if file doesn't exist
            Finally
                If (Not (reader2) Is Nothing) Then
                    reader2.Close()
                End If
            End Try
        End If
    End Sub

    Protected Overrides Sub ResetPersonalizationBlob(ByVal webPartManager As WebPartManager, ByVal path As String, ByVal userName As String)
        ' Delete the specified personalization file
        Try
            File.Delete(GetPath(userName, path))
        Catch ex As FileNotFoundException

        End Try
    End Sub

    Protected Overrides Sub SavePersonalizationBlob(ByVal webPartManager As WebPartManager, ByVal path As String, ByVal userName As String, ByVal dataBlob() As Byte)
        Dim writer As StreamWriter = Nothing
        Try
            writer = New StreamWriter(GetPath(userName, path), False)
            writer.WriteLine(Convert.ToBase64String(dataBlob))
        Finally
            If (Not (writer) Is Nothing) Then
                writer.Close()
            End If
        End Try
    End Sub

    Public Overrides Function FindState(ByVal scope As PersonalizationScope, ByVal query As PersonalizationStateQuery, ByVal pageIndex As Integer, ByVal pageSize As Integer, ByRef totalRecords As Integer) As PersonalizationStateInfoCollection
        Throw New NotSupportedException
    End Function

    Public Overrides Function GetCountOfState(ByVal scope As PersonalizationScope, ByVal query As PersonalizationStateQuery) As Integer
        Throw New NotSupportedException
    End Function

    Public Overrides Function ResetState(ByVal scope As PersonalizationScope, ByVal paths() As String, ByVal usernames() As String) As Integer
        Throw New NotSupportedException
    End Function

    Public Overrides Function ResetUserState(ByVal path As String, ByVal userInactiveSinceDate As DateTime) As Integer
        Throw New NotSupportedException
    End Function

    Private Function GetPath(ByVal userName As String, ByVal path As String) As String
        Dim sha As SHA1CryptoServiceProvider = New SHA1CryptoServiceProvider
        Dim encoding As UnicodeEncoding = New UnicodeEncoding
        Dim hash As String = Convert.ToBase64String(sha.ComputeHash(encoding.GetBytes(path))).Replace(Microsoft.VisualBasic.ChrW(47), Microsoft.VisualBasic.ChrW(95))
        If String.IsNullOrEmpty(userName) Then
            Return HttpContext.Current.Server.MapPath(String.Format("~/App_Data/Personalization_Data/{0}_Personalization.txt", hash))
        Else
            ' NOTE: Consider validating the user name here to prevent
            ' malicious user names such as "../Foo" from targeting
            ' directories other than ~/App_Data/Personalization_Data
            Return HttpContext.Current.Server.MapPath(String.Format("~/App_Data/Personalization_Data/{0}_{1}_Personalization.txt", userName.Replace(Microsoft.VisualBasic.ChrW(92), Microsoft.VisualBasic.ChrW(95)), hash))
        End If
    End Function

End Class
